package com.hzw.saas.service.email.service;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hzw.saas.api.email.IEmailLogService;
import com.hzw.saas.api.email.bo.EmailLogBO;
import com.hzw.saas.api.email.bo.EmailLogBriefBO;
import com.hzw.saas.api.email.bo.ReceiverBO;
import com.hzw.saas.api.email.constants.EmailConsts;
import com.hzw.saas.api.email.enums.EmailStatusEnum;
import com.hzw.saas.api.email.query.EmailLogAddQuery;
import com.hzw.saas.api.email.query.EmailLogSendQuery;
import com.hzw.saas.api.email.query.EmailLogUpdateQuery;
import com.hzw.saas.api.sys.dto.SysUserInfoBO;
import com.hzw.saas.api.sys.service.ISysUserService;
import com.hzw.saas.api.template.enums.TemplateTypeEnum;
import com.hzw.saas.api.user.ICustomerService;
import com.hzw.saas.api.user.bo.CustomerBO;
import com.hzw.saas.common.config.util.PageParam;
import com.hzw.saas.common.config.util.PageUtils;
import com.hzw.saas.common.util.AccountValidatorUtil;
import com.hzw.saas.common.util.MapperUtil;
import com.hzw.saas.common.util.SnowFlakeUtil;
import com.hzw.saas.common.util.exception.ConflictException;
import com.hzw.saas.common.verify.service.IEmailService;
import com.hzw.saas.service.email.mapper.EmailLogMapper;
import com.hzw.saas.service.email.model.EmailLog;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.text.StrSplitter;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import org.springframework.stereotype.Service;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
 * @author zzl
 * @since 09/08/2021
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class EmailLogServiceImpl extends ServiceImpl<EmailLogMapper, EmailLog> implements IEmailLogService, IService<EmailLog> {

    private final IEmailService emailService;
    private final ISysUserService sysUserService;
    private final ICustomerService customerService;

    @Override
    public IPage<EmailLogBriefBO> pageEmailBrief(PageParam pageParam, String from, Integer type, Integer status, String fuzzy) {
        IPage<EmailLog> entityPage = super.lambdaQuery()
            .select(EmailLog.class, emailLog -> !emailLog.getColumn().equals("content")
                && !emailLog.getColumn().equals("biz_url")
                && !emailLog.getColumn().equals("biz_title"))
            .eq(StrUtil.isNotBlank(from), EmailLog::getFrom, from)
            .eq(Objects.nonNull(type), EmailLog::getType, type)
            .eq(Objects.nonNull(status), EmailLog::getStatus, status)
            .and(StrUtil.isNotBlank(fuzzy), query -> query
                .like(EmailLog::getSubject, fuzzy)
                .or().like(EmailLog::getType, fuzzy)
                .or().like(EmailLog::getTo, fuzzy))
            .orderByDesc(EmailLog::getUpdateTime).page(pageParam.convert());
        IPage<EmailLogBriefBO> result = PageUtils.convert(entityPage, EmailLogBriefBO.class);
        this.assembleExtraInfo(result.getRecords());
        return result;
    }

    @Override
    public EmailLogBO getEmail(String pid) {
        EmailLog emailLog = super.getById(pid);
        EmailLogBO bo = MapperUtil.nf().map(emailLog, EmailLogBO.class);
        this.assembleExtraInfo(ListUtil.toList(bo));
        return bo;
    }

    @Override
    public String saveDraft(EmailLogAddQuery query) {
        EmailLog emailLog = this.convert(query);
        emailLog.setPid(SnowFlakeUtil.nextIdStr());
        super.save(emailLog);
        return emailLog.getPid();
    }

    @Override
    public boolean sendEmail(EmailLogSendQuery query) {
        //校验参数
        Assert.isTrue(StrUtil.isNotBlank(query.getSubject()), "邮件主题为空");
        Assert.isTrue(CollectionUtil.isNotEmpty(query.getTos()), "收件人为空");
        if (Objects.isNull(query.getCcs()))
            query.setCcs(new ArrayList<>());
        if (Objects.isNull(query.getBccs()))
            query.setBccs(new ArrayList<>());
        boolean match = Stream.of(query.getTos(), query.getCcs(), query.getBccs())
            .flatMap(Collection::stream).allMatch(AccountValidatorUtil::isEmail);
        Assert.isTrue(match, "邮箱地址格式异常");
        // 检查草稿是否存在
        EmailLog originDraft = null;
        if (StrUtil.isNotBlank(query.getPid())) {
            originDraft = super.getById(query.getPid());
            Assert.notNull(originDraft, "邮件草稿未找到");
        }
        // 初始化参数
        EmailLog emailLog = this.convert(query);
        if (Objects.nonNull(originDraft)) {
            MapperUtil.nf().map(emailLog, originDraft);
            emailLog = originDraft;
        }
        // 发送邮件
        try {
            log.debug("邮件开始发送 [{}] to {}", emailLog.getSubject(), emailLog.getTo());
            emailService.send(query.getTos(), query.getCcs(), query.getBccs(), query.getSubject(), query.getContent(), ObjectUtil.compare(query.getIsHtml(), 0) > 0);
            log.debug("邮件发送完成 [{}] to {}", emailLog.getSubject(), emailLog.getTo());
            emailLog.setStatus(EmailStatusEnum.SENT_SUCCESS.getCode());
        } catch (Exception e) {
            log.debug("邮件发送失败 [{}] to {}, {}", emailLog.getSubject(), emailLog.getTo(), e.getMessage());
            // 邮件发送失败
            emailLog.setStatus(EmailStatusEnum.SENT_FAILED.getCode());
        }
        if (StrUtil.isBlank(emailLog.getPid())) {
            emailLog.pureBase();
            emailLog.setPid(SnowFlakeUtil.nextIdStr());
            super.save(emailLog);
        } else {
            emailLog.pureUpdate();
            super.updateById(emailLog);
        }
        return Objects.equals(emailLog.getStatus(), EmailStatusEnum.SENT_SUCCESS.getCode());
    }

    @Override
    public boolean resendEmail(String pid, Boolean force) throws ConflictException {
        EmailLog emailLog = super.getById(pid);
        Assert.notNull(emailLog, "邮件未找到");
        boolean notSend = !Objects.equals(emailLog.getStatus(), EmailStatusEnum.SENT_SUCCESS.getCode());
        Assert.isTrue(Objects.equals(force, true) || notSend, () -> new ConflictException("邮件已发送"));
        if (notSend) {
            emailLog.pureUpdate();
        } else {
            emailLog.pureBase();
        }
        EmailLogSendQuery query = this.convert(emailLog);
        return this.sendEmail(query);
    }

    @Override
    public boolean updateDraft(EmailLogUpdateQuery query) {
        Assert.notBlank(query.getPid(), "pid为空");
        EmailLog emailLog = super.getById(query.getPid());
        Assert.notNull(emailLog, "待更新邮件不存在");
        Assert.isTrue(!Objects.equals(emailLog.getStatus(), EmailStatusEnum.SENT_SUCCESS.getCode()), "邮件已发送，无法更新");
        EmailLog entity = this.convert(query);
        return super.updateById(entity);
    }

    @Override
    public boolean deleteEmailLogs(String... pids) {
        if (ArrayUtil.isEmpty(pids)) {
            return true;
        }
        return super.removeByIds(ListUtil.toList(pids));
    }

    private void assembleExtraInfo(List<? extends EmailLogBriefBO> emailList) {
        if (CollectionUtil.isEmpty(emailList))
            return;
        List<String> fromPids = emailList.stream().map(EmailLogBriefBO::getFrom).collect(Collectors.toList());
        // 发件人(后台管理员)
        List<SysUserInfoBO> fromUser = sysUserService.listSysUsers(fromPids);
        // 收件人合集
        Set<String> receiverEmails = emailList.stream()
            .map(email -> {
                List<String> tos = StrSplitter.splitTrim(email.getTo(), EmailConsts.ADDRESS_SEPARATOR, true);
                List<String> ccs = StrSplitter.splitTrim(email.getCc(), EmailConsts.ADDRESS_SEPARATOR, true);
                List<String> bccs = StrSplitter.splitTrim(email.getBcc(), EmailConsts.ADDRESS_SEPARATOR, true);
                return Stream.of(tos, ccs, bccs).flatMap(Collection::stream).collect(Collectors.toSet());
            })
            .flatMap(Collection::stream)
            .collect(Collectors.toSet());
        List<CustomerBO> customers = customerService.listCustomerByEmails(receiverEmails);
        List<SysUserInfoBO> sysUsers = sysUserService.listSysUsersByEmail(receiverEmails);
        Map<String, String> receiverMap = customers.stream().collect(Collectors.toMap(
            CustomerBO::getEmail,
            c -> StrUtil.isBlank(c.getCustomerName()) ? c.getUserName() : c.getCustomerName(),
            (k1, k2) -> k1));
        Map<String, String> sysUserMap = sysUsers.stream().collect(Collectors.toMap(
            SysUserInfoBO::getEmail,
            s -> StrUtil.isBlank(s.getRealName()) ? s.getUserName() : s.getRealName(),
            (k1, k2) -> k1));
        receiverMap.putAll(sysUserMap);

        for (EmailLogBriefBO email : emailList) {
            // 邮件类型文本
            email.setTypeText(TemplateTypeEnum.validText(email.getType()));
            // 发件人(管理员)姓名, 为空时使用用户名
            SysUserInfoBO sysUser = fromUser.stream()
                .filter(user -> Objects.equals(user.getUserId(), email.getFrom())).findAny()
                .orElse(new SysUserInfoBO());
            email.setFromName(StrUtil.isNotBlank(sysUser.getRealName()) ? sysUser.getRealName() : sysUser.getUserName());
            // 收件人姓名，为空时使用用户名
            List<String> toEmails = StrSplitter.splitTrim(email.getTo(), EmailConsts.ADDRESS_SEPARATOR, true);
            List<ReceiverBO> tos = toEmails.stream()
                .map(to -> new ReceiverBO(to, receiverMap.getOrDefault(to, "")))
                .collect(Collectors.toList());
            email.setTos(tos);
            // 抄送人姓名，为空时使用用户名
            List<String> ccEmails = StrSplitter.splitTrim(email.getCc(), EmailConsts.ADDRESS_SEPARATOR, true);
            List<ReceiverBO> ccs = ccEmails.stream()
                .map(cc -> new ReceiverBO(cc, receiverMap.getOrDefault(cc, "")))
                .collect(Collectors.toList());
            email.setCcs(ccs);
            // 密送人姓名
            List<String> bccEmails = StrSplitter.splitTrim(email.getBcc(), EmailConsts.ADDRESS_SEPARATOR, true);
            List<ReceiverBO> bccs = bccEmails.stream()
                .map(bcc -> new ReceiverBO(bcc, receiverMap.getOrDefault(bcc, "")))
                .collect(Collectors.toList());
            email.setBccs(bccs);
        }
    }

    private EmailLog convert(EmailLogAddQuery query) {
        EmailLog emailLog = MapperUtil.nf().map(query, EmailLog.class);
        emailLog.setTo(CollectionUtil.join(query.getTos(), EmailConsts.ADDRESS_SEPARATOR));
        emailLog.setCc(CollectionUtil.join(query.getCcs(), EmailConsts.ADDRESS_SEPARATOR));
        emailLog.setBcc(CollectionUtil.join(query.getBccs(), EmailConsts.ADDRESS_SEPARATOR));
        return emailLog;
    }

    private EmailLogSendQuery convert(EmailLog emailLog) {
        EmailLogSendQuery query = MapperUtil.nf().map(emailLog, EmailLogSendQuery.class);
        query.setTos(StrSplitter.splitTrim(emailLog.getTo(), EmailConsts.ADDRESS_SEPARATOR, true));
        query.setCcs(StrSplitter.splitTrim(emailLog.getCc(), EmailConsts.ADDRESS_SEPARATOR, true));
        query.setBccs(StrSplitter.splitTrim(emailLog.getBcc(), EmailConsts.ADDRESS_SEPARATOR, true));
        return query;
    }
}
